// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-02-25

package nacos

import (
	"context"
	"fmt"
	"gitee.com/go-libs/console"
	"kv/providers"
	"os"
	"path/filepath"
)

// Provider
// 用于从Nacos下载配置文件的命令行工具.
type Provider struct {
	Command                      *console.Command
	Executor                     *providers.Executor
	Host, Path, Namespace, Group string
	Key                          Key
	Override                     bool
}

// Before
// 前置执行.
func (o *Provider) Before(_ context.Context, container *console.Container, command *console.Command) (err error) {
	var info os.FileInfo

	// 1. 读取地址.
	if g, ok := command.GetOption(optionHost); ok {
		if s := g.ToString(); s != "" {
			o.Host = s
		}
	}
	if o.Host == "" {
		o.Host = defaultHost
	}
	if !regexStartWithProtocol.MatchString(o.Host) {
		o.Host = fmt.Sprintf(`http://%s`, o.Host)
	}
	o.Host = regexEndWithSlash.ReplaceAllString(o.Host, "")

	// 2. 覆盖文件.
	if c, ok := command.GetOption(optionOverride); ok {
		o.Override = c.GetSpecified()
	}

	// 3. 读取名称.
	if g, ok := command.GetOption(optionDataId); ok {
		if s := g.ToString(); s != "" {
			o.Key = Key(s)
		}
	}
	if o.Key.IsBlank() {
		return errDataIdNotSpecified
	}

	// 4. 读取路径.
	if g, ok := command.GetOption(optionPath); ok {
		if s := g.ToString(); s != "" {
			o.Path = s
		}
	}
	if o.Path == "" {
		return errPathNotSpecified
	}
	if o.Path, err = filepath.Abs(o.Path); err != nil {
		return err
	}
	if info, err = os.Stat(o.Path); err != nil {
		// 权限错误.
		if !os.IsNotExist(err) {
			return err
		}
		// 创建目录.
		if err = os.MkdirAll(o.Path, os.ModePerm); err != nil {
			return err
		}
	} else if info != nil && !info.IsDir() {
		return errPathNotValidDirectory
	}

	// 5. 命名空间.
	if g, ok := command.GetOption(optionNamespace); ok {
		if s := g.ToString(); s != "" {
			o.Namespace = s
		}
	}

	// 6. 读取分组.
	if g, ok := command.GetOption(optionGroup); ok {
		if s := g.ToString(); s != "" {
			o.Group = s
		}
	}
	if o.Group == "" {
		o.Group = defaultGroup
	}

	// 7. 开始处理.
	container.GetOutput().Info(`+ [begin to download]`)
	container.GetOutput().Info(`  - {nacos.server.addr}: %s`, o.Host)
	container.GetOutput().Info(`  - {nacos.configuration}: namespace=%s, group=%s`, o.Namespace, o.Group)
	container.GetOutput().Info(`  - {save.file.to}: %s`, o.Path)
	container.GetOutput().Info(`  - {override.if.exists}: %v`, o.Override)
	return
}

func (o *Provider) Run(_ context.Context, container *console.Container, _ *console.Command) (err error) {
	var (
		entity *Entity
		// targets = make([]*providers.Target, 0)
	)

	// 1. 读取配置.
	container.GetOutput().Info(`+ [open content by key]`)
	if entity, err = o.Key.Get(container, o.Host, o.Group, o.Namespace); err != nil {
		return
	}

	// 2. 解析配置。
	if err = entity.Parse(container, o.Host, o.Group, o.Namespace); err != nil {
		return
	}
	return
}

// +---------------------------------------------------------------------------+
// |                                  Access                                   |
// +---------------------------------------------------------------------------+

func (o *Provider) init() *Provider {
	o.initField()
	o.initOption()
	return o
}

func (o *Provider) initField() {
	o.Command.SetDescription(providerDesc)
	o.Command.SetProvider(o)
}

func (o *Provider) initOption() {
	for k, v := range options {
		o.Command.Add(console.NewOption(k).
			SetShortName(v.Short).
			SetDescription(v.Description).
			SetKind(v.Kind).
			SetRequired(v.Required).
			SetDefaultValue(v.DefaultValue),
		)
	}
}

// Command creates a command that bind builtin provider.
func Command() *console.Command {
	return (&Provider{
		Command:  console.NewCommand(providerName),
		Executor: &providers.Executor{},
	}).init().Command
}
